using System;
using System.Collections.Generic;
using System.Diagnostics;
using NopSolutions.NopCommerce.BusinessLogic;
using NopSolutions.NopCommerce.BusinessLogic.Categories;
using NopSolutions.NopCommerce.BusinessLogic.Manufacturers;
using NopSolutions.NopCommerce.BusinessLogic.Products;
using NopSolutions.NopCommerce.BusinessLogic.Products.Specs;
using NopSolutions.NopCommerce.LuceneHelper.Queries;
using NopSolutions.NopCommerce.LuceneHelper.Tasks;

namespace NopSolutions.NopCommerce.LuceneHelper
{
    public sealed class LuceneContext
    {

        #region Singleton stuff

        static LuceneContext instance = null;
        static readonly object padlock = new object();

        LuceneContext()
        {
        }

        public static LuceneContext Instance
        {
            get
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new LuceneContext();
                    }
                    return instance;
                }
            }
        }

        #endregion

        #region Events

        public event EventHandler<FilterEventArgs> LoadFilters;

        public void OnLoadFilters(FilterEventArgs e)
        {
            if (LoadFilters != null)
                LoadFilters(this, e);
        }

        #region Products and Variants

        public void ProductUpdated(object sender, ProductEventArgs e)
        {
            Log("ProductUpdated");
            var indexManager = new IndexManager();
            indexManager.IndexTask(new ProductUpdateTask(e.Product, false));
        }
        public void ProductCreated(object sender, ProductEventArgs e)
        {
            Log("ProductCreated");
            var indexManager = new IndexManager();
            indexManager.IndexTask(new ProductUpdateTask(e.Product, false));
        }
        public void ProductVariantCreated(object sender, ProductVariantEventArgs e)
        {
            Log("ProductVariantCreated");
            var indexManager = new IndexManager();
            indexManager.IndexProduct(e.ProductVariant.Product, true);
        }
        public void ProductVariantUpdated(object sender, ProductVariantEventArgs e)
        {
            Log("ProductVariantUpdated");
            var indexManager = new IndexManager();
            indexManager.IndexTask(new ProductUpdateTask(e.ProductVariant.Product, true));
        }

        #endregion

        #region Product category mappings

        public void ProductCategoryCreated(object sender, NopEventArgs<ProductCategory> e)
        {
            Log("ProductCategoryCreated");
            var indexManager = new IndexManager();
        }

        public void ProductCategoryUpdated(object sender, NopEventArgs<ProductCategory> e)
        {
            Log("ProductCategoryUpdated");
            //Doesn't matter, so ignore event.
        }

        public void ProductCategoryDeleted(object sender, int productId, int categoryId)
        {
            Log("ProductCategoryDeleted");
            var indexManager = new IndexManager();
            indexManager.IndexTask(new ProductUpdateTask(productId, true));
        }

        #endregion

        #region Product manu mappings

        public void ProductManufacturerCreated(object sender, NopEventArgs<ProductManufacturer> e)
        {
            Log("ProductManufacturerCreated");
            var indexManager = new IndexManager();
            indexManager.IndexProduct(e.Item.Product, true);
        }

        public void ProductManufacturerUpdated(object sender, NopEventArgs<ProductManufacturer> e)
        {
            Log("ProductManufacturerUpdated");
            //who cares?
        }

        public void ProductManufacturerDeleted(object sender, int productId, int manufacturerId)
        {
            Log("ProductManufacturerDeleted");
            var indexManager = new IndexManager();
            indexManager.IndexTask(new ProductUpdateTask(productId, true));
        }

        #endregion

        #region Product spec mappings

        public void ProductSpecificationAttributeCreated(object sender, NopEventArgs<ProductSpecificationAttribute> e)
        {
            Log("ProductSpecificationAttributeCreated");
            var indexManager = new IndexManager();
            indexManager.IndexProduct(e.Item.Product, true);
            indexManager.IndexTask(new SpecificationOptionUpdateTask(e.Item.SpecificationAttributeOptionId));
        }

        public void ProductSpecificationAttributeUpdated(object sender, NopEventArgs<ProductSpecificationAttribute> e)
        {
            Log("ProductSpecificationAttributeUpdated");
            var indexManager = new IndexManager();
            indexManager.IndexProduct(e.Item.Product, true);
            indexManager.IndexTask(new SpecificationOptionUpdateTask(e.Item.SpecificationAttributeOptionId));
        }

        public void ProductSpecificationAttributeDeleted(object sender, int productId, int specificationAttributeOptionId)
        {
            Log("ProductSpecificationAttributeDeleted");
            var indexManager = new IndexManager();
            indexManager.IndexProduct(ProductManager.GetProductById(productId), true);
            indexManager.IndexTask(new SpecificationOptionUpdateTask(specificationAttributeOptionId));
        }

        #endregion

        #region Spec attributes

        public void SpecificationAttributeCreated(object sender, NopEventArgs<SpecificationAttribute> e)
        {
            Log("SpecificationAttributeCreated");
            //Who cares?
        }

        public void SpecificationAttributeUpdated(object sender, NopEventArgs<SpecificationAttribute> e)
        {
            Log("SpecificationAttributeUpdated");
            //Who cares?
        }

        public void SpecificationAttributeDeleted(object sender, int specificationAttributeId)
        {
            Log("SpecificationAttributeDeleted");
            var searchManager = new SearchManager();
            var indexManager = new IndexManager();
            var results = searchManager.SearchProducts(new ProductQuery().WithSpecification(specificationAttributeId)).MapProducts(false);
            indexManager.IndexTasks(results.ToTasks());
            indexManager.IndexTask(new SpecificationUpdateTask(specificationAttributeId));
        }

        
        #endregion

        #region Spec options

        public void SpecificationAttributeOptionCreated(object sender, NopEventArgs<SpecificationAttributeOption> e)
        {
            Log("SpecificationAttributeOptionCreated");
            //who cares?
        }

        public void SpecificationAttributeOptionUpdated(object sender, NopEventArgs<SpecificationAttributeOption> e)
        {
            Log("SpecificationAttributeOptionUpdated");
            //who cares? maybe the sort order?
        }

        public void SpecificationAttributeOptionDeleted(object sender, int specificationId, int specificationAttributeOptionId)
        {
            Log("SpecificationAttributeOptionDeleted");
            var indexManager = new IndexManager();
            var searchManager = new SearchManager();
            var results =
                searchManager.SearchProducts(new ProductQuery().WithSpecificationsOption(specificationId,
                                                                                         specificationAttributeOptionId)).MapProducts(false);
            indexManager.IndexTasks(results.ToTasks());
        }

        #endregion

        #endregion

        #region Queue

        private IList<ITask> _tasks = new List<ITask>();
        public IList<ITask> Tasks
        {
            get { return _tasks; }
        }

        public void AddTask<T>(T task) where T : ITask
        {
            AddTasks(new List<T> {task});
        }

        public void AddTasks<T>(IEnumerable<T> tasks) where T : ITask
        {
            foreach (var task in tasks)
            {
                task.AddToQueue(_tasks);
            }
            CleanQueue();
        }

        public void CleanQueue(bool forceClean)
        {
            if (!forceClean)
            {
                //we are not forcing the queue to be cleaned, so lets check and see if it should
                if (QueueEnabled && (_tasks.Count < QueueMax)) return;
            }
            var indexManager = new IndexManager();
            //tell the indexer to not use the queue because the queue is dumping these items off. we don't want the index manager to throw the items back in queue
            indexManager.IndexTasks(_tasks, false);
            _tasks.Clear();
        }

        public void CleanQueue()
        {
            CleanQueue(false);
        }

        private bool queueEnabled;
        public bool QueueEnabled
        {
            get { return queueEnabled; }
            set
            {
                if (!value)
                {
                    //we are disabling the queue, so lets force all the items in the queue to be flushed
                    CleanQueue(true);
                }
                queueEnabled = value;
            }
        }

        private int queueMax = 5;
        public int QueueMax
        {
            get { return queueMax; }
            set { queueMax = value; }
        }

        #endregion

        private void Log(string message)
        {
            Debug.WriteLine("Lucene events: " + message);
        }

    }
}
